<?php
/*****************************************************************
* Filesystem Functions Collection!
* 
* I have wrote some functions which are required many times when
* you handle files/folders.
* 
* One of the most important functions here are the create_zip,
* create_tar, extract_zip and the extract_tar .. I havn't found
* much programs offering this functionality.
* 
* @author   Mike Ramsey <mike@exocrew.com> 
* @author	ExoCrew < http://exoscripts.com, http://exocrew.com >
* @version  1.0.0
* 
* If you are going to use the functions, please do credit me 
* somewhere in the code or the site :D ..
* 
* Want to donate? ( Better do :D )
* Please join http://forums.exocrew.com and try to be an active
* participant of the community.
* 
* FOUND A BUG?
* Inform us about it by posting at http://forums.exocrew.com ..
*  
*****************************************************************/
// Are we using windows Os?
if (substr(PHP_OS,0,3) == 'WIN')
{//	define( 'IS_WIN', 1 );
}/*
 * Directory Size!
 * 
 * This function returns the directory size of the specified directory, in bytes!
 * 
 * @param   string   $dir    The full directory path including the trailing slash at end
 * @param   string   $rsize  Type of size you want returned ( bytes, kilobytes or megabytes )
 * 
 * Example Usage:
 * echo dir_size( '/home/user/public_html/myDirectory/' );
 * 
 * @return  integer  the directory size in bytes or as specified in the param
 * 
 */
function dir_size( $dir, $rsize = "" )
{	// use our other function to read the dir
	// and return the size..
	$dir_info = read_dir($dir, 0);
	$size = $dir_info['size'];
	// convert to kilobytes..
	if ($rsize == 'kb')
		$size = $size / 1024;
		// convert to megabytes..
	if ($rsize == 'mb')
		$size = $size / (1024*1024);
		// convert to gigabytes..	
	if ($rsize == 'gb')
		$size = $size / (1024*1024*1024);
		// return!
	return $size;
}/*
 * Read Directory!
 * 
 * This a function that can be used to retrieve size, list of files and list of
 * sub-directories in a directory..
 * 
 * @param   string	  $dir 		the full directory path including the trailing slash at end
 * @param   string    $collect  By default all files and folder names are collected and returned.
 * 					      		If a folder contains thousands of files and directories, the script
 *					  	  		will consume a lot of memory in the variables, in such case this
 * 					  	  		argument must be set to 0.
 * @param   integer	  $fullpaths  Set this argument to 1 if you want the full path to files/subdirs
 * @param   integer   $subdirs    Whether or not it should include files from subdirectories
 * @param	integer   $rmdir      This is set to 1 when the call is made using our del_dir function
 * 					    		  In case its set to 1, subdirectories are listed before directories
 * 
 * Example Usage:
 * $dir_info = read_dir( '/home/user/public_html/myDirectory/' );
 * echo $dir_info['size'];
 * 
 * @return  The directory size in bytes or as specified in the param
 * 
 */
function read_dir( $dir, $collect = 1, $fullpaths = 0, $subdirs = 1, $rmdir = 0 )
{	// add a trailing slash if it required..
	if (substr($dir,-1) != '/')
		$dir .= '/';
	// create arrays!
	$dirs = $files = array();
		// open the directory..
	if (!($handle = @opendir( $dir ))) {
		return false;
	}
		// read the dir..
	while( ($file = readdir($handle)) !== false )
	{
		// skip '..' and '.'
		if ($file != '..' AND $file != '.')
		{
			// if its a directory..
			if (is_dir($dir.$file) AND $subdirs == 1) {
				
				// generate the size of this sub-dir..
				$dir_info = read_dir( $dir.$file.'/', $collect, $fullpaths );
				$size += $dir_info['size'];
				
				if ($collect != 0)
				{
					if ($rmdir != 1) {
						// collect dir..
						$dirs[]  = $fullpaths == 1 ? $dir.$file : $file;
					}
					
					// merge the dirs and files array..
					$dirs  = array_merge( $dirs, $dir_info['dirs'] );
					$files = array_merge( $files, $dir_info['files'] );
					
					if ($rmdir == 1) {
						// collect dir..
						$dirs[]  = $fullpaths == 1 ? $dir.$file : $file;
					}
				}
			} else if (is_file($dir.$file)) {
				// get the size of the file..
				$size += filesize( $dir.$file );
				
				// collect file name..
				if ($collect != 0)
					$files[] = $fullpaths == 1 ? $dir.$file : $file;
			}
		}
	}
		// close the directory handle..
	@closedir( $handle );
		// clear stats cache of php..
	clearstatcache();	
		// return an array of size, directories ( sub-dirs included ) and files!
	return array( "size" => $size, "dirs" => $dirs, "files" => $files );
}/*
 * Delete Directory!
 * 
 * This function can delete a directory along with all its sub-directories and files..
 * 
 * @param	string  $dir  the full directory path including the trailing slash at end
 * 
 * Example Usage:
 * if( del_dir('/home/user/public_html/myDirectory/') )
 * {
 * 	  echo 'Directory Deleted..';
 * }
 * 
 * @return  TRUE on success
 * 
 */
function del_dir( $dir )
{	// use our other function to read the dir
	// and return the files/dirs list..
	$dir_info = read_dir($dir, 1, 1, 1, 1);
		// A loop to delete all the files first!
	foreach($dir_info['files'] as $file)
	{
		// return false on errors..
		if (!unlink($file))
		{
			return false;
		}
	}
		// A loop to delete all the subdirs..
	foreach($dir_info['dirs'] as $dirs)
	{
		// chmod the dir to 0777!
		@chmod($dirs, 0777);
		
		if (!rmdir($dirs))
		{
			return false;
		}
	}
	// chmod the dir to 0777!
	@chmod($dir, 0777);	
	// Finally! delete the main directory..
	if (!rmdir($dir))
	{
		return false;
	}
		// return true on success..
	return true;
	}
/*
 * Copy Directory!
 * 
 * This function can be used to copy a directory and all its content to another directory.
 * Simple to use and efficient!
 * 
 * @param	string  $dir      specify the directory to copy data from
 * @param   string  $copy_to  the directory to which the content will be copied
 * 
 * Example Usage:
 * copy_dir("/home/public_html/user/dir1", "/home/public_html/user/dir2");
 * 
 * @return  bool  TRUE on success, FALSE no error
 * 
 */
function copy_dir( $dir, $copy_to )
{	// if the copyto directory doesn't exists, try to create it..
	if (!is_dir($copy_to)) {
		if (!mkdir($copy_to, 0777)) {
			return false;
		}
	}
		// chmod the copyto dir to 777
	@chmod($copy_to, 0777);
		// if the directory doesnt exists..
	if (!is_dir($dir)) {
		return false;
	}
		// add a slash at end if required..
	if (substr($copy_to, -1) != '/') {
		$copy_to .= '/';
	}
	// add a slash at end if required..	
	if (substr($dir, -1) != '/') {
		$dir .= '/';
	}
		// read the directory ..
	$dir_info = read_dir($dir, 1, 1);
		// get subdir list and create them..
	foreach($dir_info['dirs'] as $dirs) {
			// remove the copy from dir name ..
		$dirs = substr($dirs, strpos($dirs, $dir) + strlen($dir));
		
		$last = "";
		// create directories if required..
		$c_dir = explode( "/", $dirs );
		foreach($c_dir as $tdir) 
		{
			// create the directory if it doesnt exists..
			if(!is_dir($copy_to. $last .$tdir)) {
				mkdir($copy_to. $last .$tdir, 0777 );
			}
			
			// set the parent dir variable..
			$last .= $tdir . '/';
		}
		
		// if it still doesnt exists..
		if (!is_dir($copy_to.$dirs)) {
			mkdir($copy_to.$dirs, 0777);
		}
		// chmod the dir to 777 so that we can write to it..
		@chmod($copy_to.$dirs, 0777);
	}
		foreach($dir_info['files'] as $file )
	{
		$fn = basename($file);  	// get the file name
		$cdir = dirname($file); 	// get the directory name
		
		// remove the "copy from" dir name ..
		$cdir = substr($cdir, strpos($cdir, $dir) + strlen($dir));
		// add a slash at end if required..
		if (substr($cdir, -1) != '/') {
			$cdir .= '/';
		}
		
		// copy!
		@copy($file, $copy_to.$cdir.$fn);
	}
		return true;
	}
/*
 * File Information!
 * 
 * Returns information about a file using a set of functions. Hey, dont remind me to use
 * stat function instead, thats just too boring..
 * 
 * The following information is returned:
 * atime - the time the file was last accessed ( unix timestamp )
 * ctime - the time the file was last changed  ( unix timestamp )
 * mtime - the time the file was last modified ( unix timestamp )
 * size  - the file size ( in bytes )
 * group - the group ID of the file
 * inode - the inode number of the file
 * owner - the user ID of the owner of the file
 * perms - the permissions on the file
 * 
 * @param	string  $file - The full file path should be provided..
 *  
 * Example Usage:
 * $file = file_info("/dir/okay.gif");
 * if ($file['size'] > 500) {
 * 	 // do stuff..
 * }
 * 
 * @return  array  File information on success, FALSE on errors!
 * 
 */
function file_info( $file )
{	// check for the file existance and the file type..
	if (!file_exists($file) OR filetype($file) != 'file') {
		return false;
	}
	// collect all the info..
	$f['atime'] = fileatime($file);
	$f['ctime'] = filectime($file);
	$f['mtime'] = filemtime($file);
	$f['size']  = filesize($file);	
	// these functions wont work on windows..
	if (IS_WIN != 1)
	{
		$f['group'] = filegroup($file);
		$f['inode'] = fileinode($file);
		$f['owner'] = fileowner($file);
		$f['perms'] = fileperms($file);
	}
		// clear stats cache..
	clearstatcache();
		// return all the collected info..
	return $f;
	}
/*
 * File Extension!
 * 
 * Grab the extension of a provided file, just to make stuff easy ;)..
 * 
 * @param	string  $file - The file name with extension
 * @param   integer $way  - There are currently two ways:
 * 							1 ( Default ) - Match using preg_match, although you must have heared
 * 								preg_match works slower, it works fine here and is also more 
 * 								efficient..
 * 							2 - If you strongly disbelieve our benchmarks and want to use the other
 * 								way available, its for you..
 * @param   integer $fwe  - Set it to 1 if you want the filename without extension instead of extension..
 * 
 * Example Usage:
 * if (file_ext($file) == 'gif') {
 * 		echo 'blah blah';
 * }
 * 
 * @return  string  File extension OR file name on success, FALSE on failure!
 * 
 */
function file_ext( $file, $way = 1, $fwe = 0 ) {
	if ($way == 1) {
		// get the file extension using preg_match!
		if (preg_match('/^(.+?)\.([a-z0-9]{1,4})$/', $file, $ext)) {
				// check whether we need to return the filename without extension
				// or the extension..
				return ($fwe == 1 ? $ext[1] : $ext[2]);
				
		} else {
				return false;
		}
	} elseif ($way == 2 ) {
		// substr upto the last dot occurance..
        $i = strrpos($file,".");
        if (!$i) { return ""; }
        $l = strlen($file) - $i;
        $ext = substr($file,$i+1,$l);
        return $ext;
	}
}/*
 * Generate An Alternate File Name!
 * 
 * Sometimes when you allow people to upload files, there can be similar file names and you will
 * need to generate an alternate file name in such case. This function allows you to easily get
 * an alternate file name either using a prefix or suffix...
 * 
 * @param	string  $file - the file name
 * @param   string  $dir  - the directory in which you want the file to upload
 * @param   string  $pre  - if you want to add a prefix to the file ( ex: _1file.gif )
 * @param   string  $suf  - if you want to add a suffix instead of prefix ( ex: file_1.gif )
 * @param   integer $delm - set it to 2 if you want the number before your defined suffix/prefix
 * 							( after setting to 2 ex: 1_file.gif OR file1_.gif )
 * 
 * Example Usage:
 * if (file_exists($yourdir.'/'.$yourfile)) {
 *       $file_name = alt_file($yourfile, $yourdir);
 * }
 * 
 * @return  string  Filename on success, FALSE on failure!
 * 
 */
function alt_file( $file, $dir, $pre = '_', $suf = null, $delm = 1 )
{	// we either need a suffix of prefix..
	if (!$suf && !$pre) {
		die( "Wrong usage of alt_file() function! You either need to define a prefix or suffix." );
	}
	// add a trailing slash if required..
	if (substr($dir,-1) != '/')
		$dir .= '/';
		// if file doesn't exists, then return..
	if (!file_exists($dir.$file))
		return $file;
	// clear stats cache..
	clearstatcache();	
	// file names are formatted as _1file.gif or file_1.gif, if you want it to check 
	// for _file.gif or file_.gif in the first loop, then comment the $x = 0 below..
	$x = 1;	
	while( true ) {
		// prepare cfile..
		if ($delm == 1) {
			$cfile = ($pre != "" ? $pre.$x : ""). file_ext($file,1,1). ($suf != "" ? $suf.$x : "") .'.'.file_ext($file,1);
		} else { 
			$cfile = ($pre != "" ? $x.$pre : ""). file_ext($file,1,1). ($suf != "" ? $x.$suf : "") .'.'.file_ext($file,1);
		}
		// check for existance..
		if (!file_exists($dir.$cfile)) {
			return $cfile;
		}
		$x++;
	}
}/*
 * Create Zip!
 * 
 * This function can be used to create a zip file. It requires the zlib module available with php.
 * 
 * @param   string  $ar_name  specify the output file.. might be a full path or just an archive name
 * @param   array   $files    an array with the file names (with path) which will be zipped
 * @param   array   $dirs     an array with the directories to include in the archive
 * @param   integer $rdata    set it to 1 if you want the function return the compressed data instead
 * 							  of writing it to a file
 * 
 * Example Usage:
 * 
 * To create a zip file with some files in it:
 * create_zip("backup.zip", array("file1.gif","file2.gif"));
 * 
 * To create a zip file with a directory and all its files and subdirs:
 * create_zip("backup.zip", array("file1.gif","file2.gif"), array("mybackupdir/"));
 * 
 * @return  bool/mixed  TRUE on success, FALSE on error OR compressed data if rdata is 1..
 * 
 */
function create_zip( $ar_name, $files = array(), $dirs = array(), $rdata = 0 )
{	// we need the zlib..
	if (!extension_loaded('zlib')) {
		die( "Zlib is required for the zip file creation! gzcompress() function required.." );
	}
		// we need one array at least..
	if (!is_array($dirs) AND !is_array($files)) {
		return false;
	}
		if (!$ar_name AND $rdata != 1) {
		return false;
	}
	$data_store = array(); // array to store compressed data
	$ctrl_dir   = array(); // array to store central dir record
	$loffset    = 0;       // last offset
		// our dir
	$cur_dir = getcwd();
		// our dir
	$cur_dir = getcwd();
		// loop and include all the files in the main directory..
	foreach( $files as $file )
    {
		// prepare the 1st directory var..
		$dir1 = substr($file, 0, strrpos($file, '/'));
		$file = substr($file, strrpos($file, '/')+1);
		// chdir because all of it needs to be added to the main ..
		@chdir($dir1);
			
		// read the file..
		$fp = @fopen($file,'r');
		$data = @fread($fp, filesize($file));
		@fclose($fp);
		// collect the zip data..
		zip_collect_data($data, $file, &$data_store, &$ctrl_dir, &$loffset);
    }
		// include all the dirs..
	foreach( $dirs as $dird )
	{
		if (substr($dird, -1) == '/') {
			$dird = substr($dird, 0, -1);
		}
		
		// prepare the 1st directory var..
		$dir1 = substr($dird, 0, strrpos($dird, '/'));
		$dird = substr($dird, strrpos($dird, '/')+1);
		// chdir because all of it needs to be added to the main ..
		chdir($dir1);
		
		// use our other function to read the dir
		// and return the files/dirs list..
		$dir_info = read_dir($dird, 1, 1, 1);
		
		if (is_array($dir_info['files']))
		  	$files2 = array_merge($files2, $dir_info['files']);
	}
		// loop and include all the files in SUB-Directories..
	if (is_array($files2)) {
		foreach( $files2 as $file )
    	{		
			// read the file..
			$fp = @fopen($file,'r');
			$data = @fread($fp, filesize($file));
			@fclose($fp);
			// collect the zip data..
			zip_collect_data($data, $file, &$data_store, &$ctrl_dir, &$loffset);
    	} // end loop
	}
		// join data_store in a variable..
	$data = implode("", $data_store );  
	$ctrldir = implode("", $ctrl_dir );  
		// prepare the final data to be written..
	$file_data = $data. $ctrldir. "\x50\x4b\x05\x06\x00\x00\x00\x00" . pack("v", sizeof($ctrl_dir)) . pack("v", sizeof($ctrl_dir)).
				 pack("V", strlen($ctrldir)) . pack("V", strlen($data)). "\x00\x00";
		// if we need to return data instead of writing it to a file..
	if ($rdata == 1) {
		return $file_data;
	}
		// change to the working dir..
	chdir($cur_dir);
		// open the file for writing ..
	if (!($fp = fopen($ar_name, 'wb'))) {
		return false;
	}
	// write to the file..
	fwrite($fp, $file_data);
	fclose($fp);
		return true;
}/*
 * Create Tar Files!
 * 
 * This function can be used to create UNCOMPRESSED tar files on the fly :) .. Some ideas were taken 
 * off Pear's tar class.
 * 
 * @param   string  $ar_name  specify the output file.. might be a full path or just an archive name
 * @param   string  $type     CURRENTLY NOT IMPLEMENTED..
 * @param   array   $files    an array with the file names (with path) which will be zipped
 * @param   array   $dirs     an array with the directories to include in the archive
 * @param   integer $rdata    set it to 1 if you want the function return the compressed data instead
 * 							  of writing it to a file
 * 
 * Example Usage:
 * 
 * To create a tar file with some files in it:
 * create_tar("backup.tar", array("file1.gif","file2.gif"));
 * 
 * To create a tar file with a directory and all its files and subdirs:
 * create_tar("backup.tar", array("file1.gif","file2.gif"), array("mybackupdir/"));
 * 
 * @return  bool/mixed  TRUE on success, FALSE on error OR compressed data if rdata is 1..
 * 
 */
function create_tar( $ar_name, $type = 'tar', $files = array(), $dirs = array(), $rdata = 0 )
{	// we need one array at least..
	if (!is_array($dirs) AND !is_array($files)) {
		return false;
	}
		// we need an archive name or the return data to be set to 1..
	if (!$ar_name AND $rdata != 1) {
		return false;
	}
		// our current working dir!
	$cur_dir = getcwd();
		// include all the dirs..
	foreach( $dirs as $dird )
	{
		if (substr($dird, -1) == '/') {
			$dird = substr($dird, 0, -1);
		}
		
		// prepare the 1st directory var..
		$dir1 = substr($dird, 0, strrpos($dird, '/'));
		$dird = substr($dird, strrpos($dird, '/')+1);
		// chdir because all of it needs to be added to the main ..
		chdir($dir1);
			
		// use our other function to read the dir
		// and return the files/dirs list..
		$dir_info = read_dir($dird, 1, 1, 1);
		
		if (is_array($dir_info['files']))
		  	$files = array_merge($files, $dir_info['files']);
	}
	// loop and include all the files..
	foreach( $files as $file )
    {
		// read the file..
		$fp = fopen($file,'r');
		$data = fread($fp, filesize($file));
		fclose($fp);
		
		// file type..
		$f_type = 0;
		
		// get the file information..
		$f_inf = file_info( $file );
		// prepare some variables..
		$size = sprintf("%11s ", decoct(strlen($data)));
		$perm  = sprintf("%6s ", decoct($f_inf['perms']));
		$uid   = sprintf("%6s ", decoct($f_inf['uid']));
		$gid   = sprintf("%6s ", decoct($f_inf['gid']));
		$mtime = sprintf("%11s ", decoct($f_inf['mtime']));		
		// create the first header with file, permissions, uid, gid, size and msdostime..
		$data_1 = pack("a100a8a8a8a12a12", $file, $perm, $uid, $gid, $size, $mtime);
		// create the second header... we aren't going to define anything over here..
		$data_2 = pack("a1a100a6a2a32a32a8a8a155a12", $f_type, "", "", "", "unknown", "unknown", "", "", "", "");
		// set checksum to 0.. 
		$checksum = 0;
		// first part of the header..
		for ($i = 0; $i < 148; $i++)
		{
			$checksum += ord(substr($data_1, $i, 1));
		}
		
		for ($i = 148; $i < 156; $i++)
		{
			$checksum += ord(' ');
		}
		// last part of the header..
		for ($i = 156, $j = 0; $i < 512; $i++, $j++)
		{
			$checksum += ord(substr($data_2, $j, 1));
		}
		// format checksum..
		$checksum = sprintf("%6s ", decoct($checksum));
		
		// start preparing output..
		$output = $data_1;
		$output .= pack( "a8", $checksum ) . $data_2 . $data;
		
		// file size again..		
		$size = strlen($data);
		
		// a tar header is 512 bytes, so keep adding null bytes to make it 512...
		if ($size > 0 AND ($size % 512) != 0) {
			$output .= str_repeat("\0", 512 - ($size % 512));
		}
		// add it to file_data..
		$file_data .= $output;
		// unset..
		unset($output);		
    }
		// tar file footer..
	$file_data .= pack( "a512", "" );
		// if we need to return data instead of writing it to a file..
	if ($rdata == 1) {
		return $file_data;
	}
		// change to the working dir..
	chdir($cur_dir);
		// open the file for writing ..
	if (!($fp = fopen($ar_name, 'wb'))) {
		return false;
	}
	// write to the file..
	fwrite($fp, $file_data);
	fclose($fp);
		return true;
}/*
 * Extract Zip Files!
 * 
 * I have been searching for something similar for a while and couldn't find one that suits my need. 
 * There is something called PCLZip or whatever it is, but that just is too messed up and will never
 * help someone understand how zip files are extracted. 
 * 
 * I have created this function as simple as possible. Its very easy to understand and use :)
 * 
 * Official zip format: http://www.pkware.com/company/standards/appnote/index.php
 * 
 * @param   string  $file    full path to the zip file which needs to be extracted..
 * @param   string  $ex_dir  you can specify an output directory if you wish..
 * 
 * Example Usage:
 * ex 1:
 * extract_zip("file.zip");
 * ex 2:
 * extract_zip("file.zip", "mydirectory/");
 * 
 * @return  bool  TRUE on success, FALSE on error
 * 
 */
function extract_zip( $file, $ex_dir = null )
{	// check to extract directory existance..
	if ($ex_dir != null AND !is_dir($ex_dir)) {
		return false;
	}
		// replace backslashes with forward ones..
	$ex_dir = str_replace( '\\', '/', $ex_dir );
		// if we dont have an ending slash, add it..
	if (substr($ex_dir, -1) != "/") {
		$ex_dir .= '/';
	}
		// open the zip file for reading..
	$fp = fopen($file, 'rb');
	while (true)
	{
		$f++;
		
		// read 30 chars data from the zip file..
		$data = fread($fp, 30);
		
		// get the id..
		$sig  = substr($data, 0, 4);
		
		// check if need to break..
		if ($sig != "\x50\x4b\x03\x04") {
			break;
		}
		
		// unpack the headers ..
		$unpack = unpack("vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len", substr($data, 4));
		
		// store all the information..
		$files[$f]['size']    = $unpack['compressed_size'];
		$files[$f]['fn_size'] = $unpack['filename_len'];
		$files[$f]['crc32']	  = $unpack['crc'];
		$files[$f]['compression'] = $unpack['compression'];
		
		// get the file name and the file data..
		$files[$f]['name']    = fread($fp, $files[$f]['fn_size']);
		$files[$f]['data']    = fread($fp, $files[$f]['size']);
		
		// decompress if possible..
		if (!empty($files[$f]['data']) && $files[$f]['compression'] == 8) {
			$files[$f]['data'] = @gzinflate( $files[$f]['data'] );
		} else if(!empty($files[$f]['data']) && $files[$f]['compression'] == 12) {
			$files[$f]['data'] = @bzdecompress( $files[$f]['data'] );
		}
		
	}
		// close the handler..
	fclose($fp);
	// loop through each files we collected! Extract and create directories, subdirectories, and 
	// the files..
	foreach ($files as $file) 
	{
		$file_name = basename($file['name']);   // get the file name
		$dir_name  = dirname($file['name']);    // get the directory name
		
		// if we dont have a file name or we dont have the file 
		// data, then its either a dir record or something is wrong..
		if (!$file_name OR !$file['data']) {
			continue;
		}
		
		$last = "";
		// create directories if required..
		$c_dir = explode( "/", $dir_name );
		foreach($c_dir as $dir) 
		{
			// create the directory if it doesnt exists..
			if(!is_dir($ex_dir. $last .$dir)) {
				mkdir( $ex_dir. $last .$dir, 0777 );
			}
			
			// set the parent dir variable..
			$last .= $dir . '/';
		}
		
		// try to create a new file..
		if (!($fp = fopen($ex_dir . $dir_name .'/'. $file_name, 'wb'))) {
			return false;
		}
		
		// write to the file and close the handler..
		fwrite($fp, $file['data']);
		fclose($fp);
	}
		return true;
}/*
 * Extract Tar Files!
 * 
 * Although you might be wondering why would you need to use this when you can use the pear's
 * one. The pear's tar class might be too difficult to understand for some newbies, mine one is 
 * pretty easy to understand and use.
 * 
 * @param   string  $file    full path to the zip file which needs to be extracted..
 * @param   string  $ex_dir  you can specify an output directory if you wish..
 * 
 * Example Usage:
 * ex 1:
 * extract_tar("file.tar");
 * ex 2:
 * extract_tar("file.tar", "mydirectory/");
 * 
 * @return  bool  TRUE on success, FALSE on error
 * 
 */
function extract_tar( $file, $ex_dir = null )
{	// check to extract directory existance..
	if ($ex_dir != null AND !is_dir($ex_dir)) {
		return false;
	}
		// replace backslashes with forward ones..
	$ex_dir = str_replace( '\\', '/', $ex_dir );
		// if we dont have an ending slash, add it..
	if (substr($ex_dir, -1) != "/") {
		$ex_dir .= '/';
	}
		// open the tar file for reading..
	$fp = fopen($file, 'rb');
	while (!feof($fp))
	{
		$f++;
		// read a block of 512 bytes..
		$data = fread($fp, 512);
		
		$checksum = 0;
		// first part of the header..
		for ($i = 0; $i < 148; $i++)
		{
			$checksum += ord(substr($buffer, $i, 1));
		}
		
		for ($i = 148; $i < 156; $i++)
		{
			$checksum += ord(' ');
		}
		// last part of the header..
		for ($i = 156, $j = 0; $i < 512; $i++, $j++)
		{
			$checksum += ord(substr($buffer, $j, 1));
		}
		
		// unpack the headers..
		$unpack = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $data);
		
		// checksum in this block..
		$files[$f]['checksum'] = octdec(trim($unpack['checksum']));
		
		// we have reached end of the file.. empty block..
		if ($checksum == 256 && $files[$f]['checksum'] == 0) {
			break;
		}
		
		// trim typeflag, and we only support 0 typeflag so confirm that this block has
		// a typeflag of 0..
		$unpack['typeflag'] = trim($unpack['typeflag']);
		if ($unpack['typeflag'] != "0") {
			continue;
		}
			// prepare file related variables..
		$files[$f]['name'] = trim($unpack['filename']);
		$files[$f]['size'] = octdec(trim($unpack['size']));
		$files[$f]['data'] = fread($fp, $files[$f]['size']);
		
		// sizes need to be same..
		if (strlen($files[$f]['data']) != $files[$f]['size']) {
			return false;
		}
		
		// if a total of 512 wasn't read , read it now to make up a block..
		fread($fp, 512 - ($files[$f]['size'] % 512));
			
	}
		// close the handler..
	fclose($fp);
	// loop through each files we collected! Extract and create directories, subdirectories, and 
	// the files..
	foreach ($files as $file) 
	{
		$file_name = basename($file['name']);   // get the file name
		$dir_name  = dirname($file['name']);    // get the directory name
		
		// if we dont have a file name or we dont have the file 
		// data, then its either a dir record or something is wrong..
		if (!$file_name OR !$file['data']) {
			continue;
		}
		
		$last = "";
		// create directories if required..
		$c_dir = explode( "/", $dir_name );
		foreach($c_dir as $dir) 
		{
			// create the directory if it doesnt exists..
			if(!is_dir($ex_dir. $last .$dir)) {
				mkdir( $ex_dir. $last .$dir, 0777 );
			}
			
			// set the parent dir variable..
			$last .= $dir . '/';
		}
		
		// try to create a new file..
		if (!($fp = fopen($ex_dir . $dir_name .'/'. $file_name, 'wb'))) {
			return false;
		}
		
		// write to the file and close the handler..
		fwrite($fp, $file['data']);
		fclose($fp);
	}
		return true;
}//----------------------------------------------------
// !! MEMBER FUNCTIONS !!
// Below are the member functions of the functions 
// defined above..
//----------------------------------------------------
/*
 * Zip file collection and compression!
 * 
 * This function is a member function of create_zip, used to collect and compress data.
 * Some of the code is based on a script that I found somewhere long ago by Eric Mueller..
 * 
 * @param   mixed   $data   file data that needs to be compressed
 * @param   string  $name   name of the file that is being compressed
 * @param   pointer $data_store  a pointer to an array that will be used to collect compressed data
 * @param   pointer $ctrl_dir    a pointer to the ctrl_dir array that will be used to collect ctrl_dir
 * 								 related data..
 * @param   pointer $loffset     last offset pointer
 * 
 * Example Usage:
 * Not available..
 * 
 * @access  private
 * @see     create_zip()
 */
function zip_collect_data($data, $name, &$data_store, &$ctrl_dir, &$loffset)
{	$name = str_replace("\\", "/", $name);
		// file header sig..
	$fr   = "\x50\x4b\x03\x04";
	// version needed to extract the file..
	$fr   .= "\x14\x00" . "\x00\x00";
	// compression method..
	$fr   .= "\x08\x00";
		// prepare dos time..
    $d_time = date_unix2dos( time() );
	$dtime  = pack('v', $d_time[1]);
	$dtime .= pack('v', $d_time[0]);
		// add the modification date/time header..
	$fr   .= $dtime;
	// "local file header" segment
	$unc_len = strlen($data);
	$crc     = crc32($data);
	$zdata   = gzcompress($data,9);
	$zdata   = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
	$c_len   = strlen($zdata);
	$fr      .= pack('V', $crc);             // crc32
	$fr      .= pack('V', $c_len);           // compressed filesize
	$fr      .= pack('V', $unc_len);         // uncompressed filesize
	$fr      .= pack('v', strlen($name));    // length of filename
	$fr      .= pack('v', 0);                // extra field length
	$fr      .= $name;
	// "file data" segment
	$fr .= $zdata;
	// "data descriptor" segment (optional but necessary if archive is not served as file)
	$fr .= pack('V', $crc);                 // crc32
	$fr .= pack('V', $c_len);               // compressed filesize
	$fr .= pack('V', $unc_len);             // uncompressed filesize
	// add this entry to array
	$data_store[] = $fr;
	$new_offset   = strlen(implode('', $data_store));
	// now add to central directory record
	$cdrec = "\x50\x4b\x01\x02";
	$cdrec .= "\x00\x00";                // version made by
	$cdrec .= "\x14\x00";                // version needed to extract
	$cdrec .= "\x00\x00";                // gen purpose bit flag
	$cdrec .= "\x08\x00";                // compression method
	$cdrec .= $dtime;        			 // last mod time & date
	$cdrec .= pack('V', $crc);           // crc32
	$cdrec .= pack('V', $c_len);         // compressed filesize
	$cdrec .= pack('V', $unc_len);       // uncompressed filesize
	$cdrec .= pack('v', strlen($name));  // length of filename
	$cdrec .= pack('v', 0);              // extra field length
	$cdrec .= pack('v', 0);              // file comment length
	$cdrec .= pack('v', 0);              // disk number start
	$cdrec .= pack('v', 0);              // internal file attributes
	$cdrec .= pack('V', 32 );            // external file attributes - 'archive' bit set
	$cdrec .= pack('V', $loffset); // relative offset of local header
	$loffset = $new_offset;
	$cdrec .= $name;
	// optional extra field, file comment goes here save to central directory
	$ctrl_dir[] = $cdrec;
}/*
 * Unix Date/Time conversion to Dos
 * 
 * Zip files use the msdos based time! This functions is created to serve the unix-msdos 
 * date and time conversion needs.
 * 
 * @param	string  $timestamp  a unix timestamp
 * 
 * Example Usage:
 * $dtime = date_unix2dos(time());
 * 
 * @return  array  [0] => dos date, [1] => dos time
 * @see     create_zip()
 */
function date_unix2dos( $timestamp )
{	if (!$timestamp) {
		$timestamp = time();
	}
		// get date ...
	$date = getdate($timestamp);
	$hour = $date['hours'];
	// Lsh 11 on hour.. ( hour * (2^11) )
	$hour = $hour << 11;
		// Lsh 5 on mins.. ( mins * (2^5) )
	$mins = $date['minutes'] << 5;
		// divide seconds by 2
	$secs = round($date['seconds'] / 2);
	// generate ms-dos based time!
	$time = $hour + $mins + $secs;
		// return [0] => date, [1] => time ..
	return array( ((($date['year']-1980) * 512) + ($date['mon'] * 32) + $date['mday']), $time );
}?>